package com.partybuilding.aspect;

import com.partybuilding.annotation.PrivacyEncryptField;
import com.partybuilding.annotation.PrivacyEncryptMethod;
import com.partybuilding.domain.ResponseResult;
import com.partybuilding.enums.DesensitizationEnum;
import com.partybuilding.utils.PrivacyUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

import static java.lang.System.in;

/**
 * @author wulala
 * 切面类
 */
@Component
@Aspect
@Slf4j
public class EncryptAspect {


    @Pointcut("@annotation(com.partybuilding.annotation.PrivacyEncryptMethod)")
    public void pt(){
        
    }
    @Around("pt()")
    public ResponseResult PrivacyEncrypt(ProceedingJoinPoint joinPoint) throws Throwable {
        ResponseResult result;

        //获取被增强方法上的注解对象
        PrivacyEncryptMethod privacyEncryptMethod=getPrivacyEncryptMethod(joinPoint);
            try {

                ResponseResult proceed = (ResponseResult)joinPoint.proceed();
                //若切面注解标记需要脱敏则进行脱敏，否则直接返回传输值
                if (privacyEncryptMethod.desensitize()==DesensitizationEnum.DESENSITIZE) {

                    result = handleAfter(proceed);
                }else {
                    result=proceed;
                }
            } finally {
                ;
            }
        return result;
    }

    /**
     * @param joinPoint
     * 获取被增强方法上的注解对象
     * @return
     */
    private PrivacyEncryptMethod getPrivacyEncryptMethod(ProceedingJoinPoint joinPoint) {
        MethodSignature methodSignature=(MethodSignature) joinPoint.getSignature();
        PrivacyEncryptMethod privacyEncryptMethod = methodSignature.getMethod().getAnnotation(PrivacyEncryptMethod.class);
        return privacyEncryptMethod;
    }


    /**
     * @param proceed 在执行目标方法后对结果进行脱敏操作
     * @return
     */

    private ResponseResult handleAfter(ResponseResult proceed) throws IllegalAccessException {

        ResponseResult result = null;

        if (proceed.getData() instanceof String) {
            decryptValue();
        } else {
            proceed.setData(decryptData(proceed.getData()));
            result =proceed;
        }
        return result;
    }






    private Object decryptData(Object obj) throws IllegalAccessException {

        //若为空则返回空集
        if (Objects.isNull(obj)) {
            return null;
        }
        //若为集合则进行集合脱敏
        if (obj instanceof ArrayList) {
            decryptList(obj);
        } else {
            //否则进行单个对象脱敏
            decryptObj(obj);
        }
        return obj;
    }

    private void decryptObj(Object obj) throws IllegalAccessException {
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            //判断每个属性是否包含脱敏注解类
            Boolean hasSecureField = field.isAnnotationPresent(PrivacyEncryptField.class);
            //如果存在则对该字段进行脱敏
            if (hasSecureField) {
                field.setAccessible(true);
                if (field.get(obj) != null) {
                    String realValue = (String) field.get(obj);
                    //获取对应需要脱敏的属性的类型
                    DesensitizationEnum desensitizationEnum = field.getAnnotation(PrivacyEncryptField.class).type();
                    String value = PrivacyUtil.desensitize(realValue,desensitizationEnum);
                    field.set(obj, value);
                }
            }
        }
    }

    private void decryptList(Object obj) throws IllegalAccessException {
        List<Object> result = new ArrayList<>();
        if (obj instanceof ArrayList) {
            result.addAll((Collection<?>) obj);
        }
        for (Object object : result) {
            decryptObj(object);
        }
    }

    private void decryptValue() {
        log.info("根据对象进行解密脱敏，单个字段不做处理！");
    }


}
